// Copyright (c) 2019, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// Dart test program for testing dart:ffi function pointers with callbacks.
//
// VMOptions=
// VMOptions=--stacktrace-every=100
// VMOptions=--use-slow-path
// VMOptions=--use-slow-path --stacktrace-every=100
// VMOptions=--dwarf_stack_traces --no-retain_function_objects --no-retain_code_objects
// VMOptions=--test_il_serialization
// VMOptions=--profiler --profile_vm=true
// VMOptions=--profiler --profile_vm=false
// SharedObjects=ffi_test_functions

import 'dart:ffi';

import 'callback_tests_utils.dart';

typedef SimpleAdditionType = Int32 Function(Int32, Int32);
int simpleAddition(int x, int y) {
  print("simpleAddition($x, $y)");
  return x + y;
}

class Foo {
  static int simpleAddition(int x, int y) {
    print("Foo.simpleAddition($x, $y)");
    return x + y;
  }
}

typedef IntComputationType = Int64 Function(Int8, Int16, Int32, Int64);
int intComputation(int a, int b, int c, int d) {
  print("intComputation($a, $b, $c, $d)");
  return d - c + b - a;
}

typedef UintComputationType = Uint64 Function(Uint8, Uint16, Uint32, Uint64);
int uintComputation(int a, int b, int c, int d) {
  print("uintComputation($a, $b, $c, $d)");
  return d - c + b - a;
}

typedef SimpleMultiplyType = Double Function(Double);
double simpleMultiply(double x) {
  print("simpleMultiply($x)");
  return x * 1.337;
}

typedef SimpleMultiplyFloatType = Float Function(Float);
double simpleMultiplyFloat(double x) {
  print("simpleMultiplyFloat($x)");
  return x * 1.337;
}

typedef ManyIntsType =
    IntPtr Function(
      IntPtr,
      IntPtr,
      IntPtr,
      IntPtr,
      IntPtr,
      IntPtr,
      IntPtr,
      IntPtr,
      IntPtr,
      IntPtr,
    );
int manyInts(
  int a,
  int b,
  int c,
  int d,
  int e,
  int f,
  int g,
  int h,
  int i,
  int j,
) {
  print("manyInts($a, $b, $c, $d, $e, $f, $g, $h, $i, $j");
  return a + b + c + d + e + f + g + h + i + j;
}

typedef ManyDoublesType =
    Double Function(
      Double,
      Double,
      Double,
      Double,
      Double,
      Double,
      Double,
      Double,
      Double,
      Double,
    );
double manyDoubles(
  double a,
  double b,
  double c,
  double d,
  double e,
  double f,
  double g,
  double h,
  double i,
  double j,
) {
  print("manyDoubles($a, $b, $c, $d, $e, $f, $g, $h, $i, $j");
  return a + b + c + d + e + f + g + h + i + j;
}

typedef ManyArgsType =
    Double Function(
      IntPtr,
      Float,
      IntPtr,
      Double,
      IntPtr,
      Float,
      IntPtr,
      Double,
      IntPtr,
      Float,
      IntPtr,
      Double,
      IntPtr,
      Float,
      IntPtr,
      Double,
      IntPtr,
      Float,
      IntPtr,
      Double,
    );
double manyArgs(
  int _1,
  double _2,
  int _3,
  double _4,
  int _5,
  double _6,
  int _7,
  double _8,
  int _9,
  double _10,
  int _11,
  double _12,
  int _13,
  double _14,
  int _15,
  double _16,
  int _17,
  double _18,
  int _19,
  double _20,
) {
  print(
    "manyArgs( $_1, $_2, $_3, $_4, $_5, $_6, $_7, $_8, $_9, $_10," +
        "$_11, $_12, $_13, $_14, $_15, $_16, $_17, $_18, $_19, $_20)",
  );
  return _1 +
      _2 +
      _3 +
      _4 +
      _5 +
      _6 +
      _7 +
      _8 +
      _9 +
      _10 +
      _11 +
      _12 +
      _13 +
      _14 +
      _15 +
      _16 +
      _17 +
      _18 +
      _19 +
      _20;
}

typedef StoreType = Pointer<Int64> Function(Pointer<Int64>);
Pointer<Int64> store(Pointer<Int64> ptr) => (ptr + 1)..value = 1337;

typedef NullPointersType = Pointer<Int64> Function(Pointer<Int64>);
Pointer<Int64> nullPointers(Pointer<Int64> ptr) => (ptr + 1);

typedef ReturnVoid = Void Function();
void returnVoid() {}

void throwException() {
  throw "Exception.";
}

typedef ThrowExceptionInt = IntPtr Function();
int throwExceptionInt() {
  throw "Exception.";
}

typedef ThrowExceptionDouble = Double Function();
double throwExceptionDouble() {
  throw "Exception.";
}

typedef ThrowExceptionPointer = Pointer<Void> Function();
Pointer<Void> throwExceptionPointer() {
  throw "Exception.";
}

typedef TakeMaxUint8x10Type =
    IntPtr Function(
      Uint8,
      Uint8,
      Uint8,
      Uint8,
      Uint8,
      Uint8,
      Uint8,
      Uint8,
      Uint8,
      Uint8,
    );
int takeMaxUint8x10(
  int a,
  int b,
  int c,
  int d,
  int e,
  int f,
  int g,
  int h,
  int i,
  int j,
) {
  print("takeMaxUint8x10($a, $b, $c, $d, $e, $f, $g, $h, $i, $j)");
  return a == 0xff &&
          b == 0xff &&
          c == 0xff &&
          d == 0xff &&
          e == 0xff &&
          f == 0xff &&
          g == 0xff &&
          h == 0xff &&
          i == 0xff &&
          j == 0xff
      ? 1
      : 0;
}

typedef ReturnMaxUint8Type = Uint8 Function();
int returnMaxUint8() {
  return 0xff;
}

int returnMaxUint8v2() {
  return 0xabcff;
}

final testcases = [
  CallbackTest(
    "SimpleAddition",
    Pointer.fromFunction<SimpleAdditionType>(simpleAddition, 0),
  ),
  CallbackTest(
    "SimpleAddition",
    Pointer.fromFunction<SimpleAdditionType>(Foo.simpleAddition, 0),
  ),
  CallbackTest(
    "IntComputation",
    Pointer.fromFunction<IntComputationType>(intComputation, 0),
  ),
  CallbackTest(
    "UintComputation",
    Pointer.fromFunction<UintComputationType>(uintComputation, 0),
  ),
  CallbackTest(
    "SimpleMultiply",
    Pointer.fromFunction<SimpleMultiplyType>(simpleMultiply, 0.0),
  ),
  CallbackTest(
    "SimpleMultiplyFloat",
    Pointer.fromFunction<SimpleMultiplyFloatType>(simpleMultiplyFloat, 0.0),
  ),
  CallbackTest("ManyInts", Pointer.fromFunction<ManyIntsType>(manyInts, 0)),
  CallbackTest(
    "ManyDoubles",
    Pointer.fromFunction<ManyDoublesType>(manyDoubles, 0.0),
  ),
  CallbackTest("ManyArgs", Pointer.fromFunction<ManyArgsType>(manyArgs, 0.0)),
  CallbackTest("Store", Pointer.fromFunction<StoreType>(store)),
  CallbackTest(
    "NullPointers",
    Pointer.fromFunction<NullPointersType>(nullPointers),
  ),
  CallbackTest("ReturnVoid", Pointer.fromFunction<ReturnVoid>(returnVoid)),
  CallbackTest(
    "ThrowExceptionDouble",
    Pointer.fromFunction<ThrowExceptionDouble>(throwExceptionDouble, 42.0),
  ),
  CallbackTest(
    "ThrowExceptionPointer",
    Pointer.fromFunction<ThrowExceptionPointer>(throwExceptionPointer),
  ),
  CallbackTest(
    "ThrowException",
    Pointer.fromFunction<ThrowExceptionInt>(throwExceptionInt, 42),
  ),
  CallbackTest(
    "TakeMaxUint8x10",
    Pointer.fromFunction<TakeMaxUint8x10Type>(takeMaxUint8x10, 0),
  ),
  CallbackTest(
    "ReturnMaxUint8",
    Pointer.fromFunction<ReturnMaxUint8Type>(returnMaxUint8, 0),
  ),
  CallbackTest(
    "ReturnMaxUint8",
    Pointer.fromFunction<ReturnMaxUint8Type>(returnMaxUint8v2, 0),
  ),
];

void main() {
  testcases.forEach((t) => t.run());
}
